/*
 * @Author: tanjunyi@doctorwork.com
 * @Date: 2020-05-02 18:15:34
 * @LastEditors: tanjunyi@doctorwork.com
 * @LastEditTime: 2020-05-02 18:15:34
 * @Description:
 */
/*
 * @Author: tanjunyi@doctorwork.com
 * @Date: 2020-04-10 09:49:05
 * @LastEditors: tanjunyi@doctorwork.com
 * @LastEditTime: 2020-05-02 14:45:23
 * @Description:
 */

let systemFlag = 1;
/**
 * 收集所有回调函数，side-effects
 *
	{
		ObservableObject@1 : {
			num : ()=>{
				callback()
			},
		}
 	}
 *
 */
let observer = {};
let classOberver = {};
let globalState = {
	mobxGuid : 0
}

/**
 * 临时存储副作用
 */
let observerTemp = {};
var mobxPendingDecorators = Symbol("mobx pending decorators");

var enumerableDescriptorCache = {};// class 缓存，截取 get,bundleRenderer.renderToStream
var $mobx = Symbol("mobx administration");

function __read(o, n) {
	var m = typeof Symbol === "function" && o[Symbol.iterator];
	if (!m) return o;
	var i = m.call(o), r, ar = [], e;
	try {
		while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
	}
	catch (error) { e = { error: error }; }
	finally {
		try {
			if (r && !r.done && (m = i["return"])) m.call(i);
		}
		finally { if (e) throw e.error; }
	}
	return ar;
}
function __values(o) {
	var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
	if (m) return m.call(o);
	return {
		next: function () {
			if (o && i >= o.length) o = void 0;
			return { value: o && o[i++], done: !o };
		}
	};
}

function __spread() {
	for (var ar = [], i = 0; i < arguments.length; i++)
		ar = ar.concat(__read(arguments[i]));
	return ar;
}

var Atom = /** @class */ (function () {
    /**
     * Create a new atom. For debugging purposes it is recommended to give it a name.
     * The onBecomeObserved and onBecomeUnobserved callbacks can be used for resource management.
     */
	function Atom(name) {
		// if (name === void 0) { name = "Atom@" + getNextId(); }
		this.name = name;
		this.isPendingUnobservation = false; // for effective unobserving. BaseAtom has true, for extra optimization, so its onBecomeUnobserved never gets called, because it's not needed
		this.isBeingObserved = false;
		this.observers = new Set();
		this.diffValue = 0;
		this.lastAccessedBy = 0;
		// this.lowestObserverState = exports.IDerivationState.NOT_TRACKING;
	}
	return Atom;
})()
function stringifyKey(key) {
	if (key && key.toString)
		return key.toString();
	else
		return new String(key).toString();
}
var observablePropertyConfigs = Object.create(null);
function generateObservablePropConfig(propName) {
	return (observablePropertyConfigs[propName] ||
		(observablePropertyConfigs[propName] = {
			configurable: true,
			enumerable: true,
			get: function () {
				return this[$mobx].read(propName);
			},
			set: function (v) {
				this[$mobx].write(propName, v);
			}
		}));
}

var ObservableValue = /** @class */ (function () {

	class ObservableValue{
		constructor(name, value){
			this.value = value;
		this.name = name;
		}
		get ()  {
			return this.value
		}
		set (newValue)  {
			this.value = newValue
		}
	}
	ObservableValue.prototype.prepareNewValue = function (newValue) {
		return this.value;
	};
	ObservableValue.prototype.setNewValue = function (newValue) {
		var oldValue = this.value;
		this.value = newValue;
	};
	return ObservableValue;
})()



var ObservableObjectAdministration = /** @class */ (function () {
	function ObservableObjectAdministration(target, values, name, defaultEnhancer) {
		if (values === void 0) { values = new Map(); }
		this.target = target;
		this.values = values;
		this.name = name;
		this.defaultEnhancer = defaultEnhancer;
		this.keysAtom = new Atom(name + ".keys");
	}
	ObservableObjectAdministration.prototype.read = function (key) {
		debugger
		return this.values.get(key).value;
	};
	ObservableObjectAdministration.prototype.write = function (key, newValue) {
		debugger
		var observable = this.values.get(key);
		observable.set(newValue);
		newValue = observable.prepareNewValue(newValue);
		observable.setNewValue(newValue);
	};
	ObservableObjectAdministration.prototype.addObservableProp = function (propName, newValue, enhancer) {
		if (enhancer === void 0) { enhancer = this.defaultEnhancer; }
		var target = this.target;

		var observable = new ObservableValue(propName, newValue);
		this.values.set(propName, observable);
		newValue = observable.value; // observableValue might have changed it
		Object.defineProperty(target, propName, generateObservablePropConfig(propName));
		// this.notifyPropertyAddition(propName, newValue);
	};
	return ObservableObjectAdministration;
})()

function getNextId() {
    return ++globalState.mobxGuid;
}
function asObservableObject(target, name, defaultEnhancer) {
	if (Object.prototype.hasOwnProperty.call(target, $mobx))
		return target[$mobx];
		if (!name)
        name = "ObservableObject@" + getNextId();
	var adm = new ObservableObjectAdministration(target, new Map(), stringifyKey(name), defaultEnhancer);
	addHiddenProp(target, $mobx, adm);
	return adm;
}

function createDecoratorForEnhancer(enhancer) {
	var decorator = createPropDecorator(true, function (target, propertyName, descriptor, _decoratorTarget, decoratorArgs) {
		var initialValue = descriptor
			? descriptor.initializer
				? descriptor.initializer.call(target)
				: descriptor.value
			: undefined;
		asObservableObject(target).addObservableProp(propertyName, initialValue, enhancer);
		// FIX:
		// target[propertyName] = initialValue;
	});
	var res = function observableDecorator() {
		// This wrapper function is just to detect illegal decorator invocations, deprecate in a next version
		// and simply return the created prop decorator
		if (arguments.length < 2)
			return fail("Incorrect decorator invocation. @observable decorator doesn't expect any arguments");
		return decorator.apply(null, arguments);
	}
	res.enhancer = enhancer;
	return res;
}
function addHiddenProp(object, propName, value) {
	Object.defineProperty(object, propName, {
		enumerable: false,
		writable: true,
		configurable: true,
		value: value
	});
}
var mobxDidRunLazyInitializersSymbol = Symbol("mobx did run lazy initializers");
function initializeInstance(target) {
	var e_1, _a;
	if (target[mobxDidRunLazyInitializersSymbol] === true)
		return;
	var decorators = target[mobxPendingDecorators];
	if (decorators) {
		addHiddenProp(target, mobxDidRunLazyInitializersSymbol, true);
		// Build property key array from both strings and symbols
		var keys = __spread(Object.getOwnPropertySymbols(decorators), Object.keys(decorators));
		for (var keys_1 = __values(keys), keys_1_1 = keys_1.next(); !keys_1_1.done; keys_1_1 = keys_1.next()) {
			var key = keys_1_1.value;
			var d = decorators[key];
			d.propertyCreator(target, d.prop, d.descriptor, d.decoratorTarget, d.decoratorArguments);
		}

	}

}
function createPropertyInitializerDescriptor(prop, enumerable) {
	var cache = enumerableDescriptorCache;
	return (cache[prop] ||
		(cache[prop] = {
			configurable: true,
			enumerable: enumerable,
			get: function () {
				initializeInstance(this);
				return this[prop];
			},
			set: function (value) {
				initializeInstance(this);
				this[prop] = value;
			}
		}));
}
function createPropDecorator(propertyInitiallyEnumerable, propertyCreator) {
	return function decoratorFactory() {
		var decoratorArguments;
		var decorator = function decorate(target, prop, descriptor, applyImmediately) {
			if (!Object.prototype.hasOwnProperty.call(target, mobxPendingDecorators)) {
				var inheritedDecorators = target[mobxPendingDecorators];
				addHiddenProp(target, mobxPendingDecorators, Object.assign({}, inheritedDecorators));
			}
			target[mobxPendingDecorators][prop] = {
				prop: prop,
				propertyCreator: propertyCreator,
				descriptor: descriptor,
				decoratorTarget: target,
				decoratorArguments: decoratorArguments
			};
			return createPropertyInitializerDescriptor(prop, propertyInitiallyEnumerable);
		}
		return decorator.apply(null, arguments);
	}
}

// descriptor
// 	? descriptor.initializer
// 		? descriptor.initializer.call(obj)
// 		: descriptor.value
// 	: undefined;
var deepDecorator = createDecoratorForEnhancer();
function observable(obj, prop, descriptor) {
	// 区分传入的参数是类还是对象
	if (typeof prop == 'string') {
		console.log(`%c 当前传入为类, 监听属性：${prop}`, "color:#0f0")
		return deepDecorator.apply(null, arguments);
	} else {
		let observeObj = `ObservableObject@${systemFlag++}`
		var proxy = new Proxy(obj, {
			get: (obj, prop, receiver) => {
				// 收集依赖
				if (isCollect && (typeof prop == 'string')) {
					// 临时存储所有的回调 ,例如： { key1 : callback1 }
					let ObserveFlag = receiver[Object.getOwnPropertySymbols(obj)[0]]
					observerTemp[ObserveFlag] = {
						[prop]: currentCallback
					}
				}
				// 准备回调
				if (listenerFlag && (typeof prop == 'string')) {
					// 存储所用的keys ,例如：['key1','key2']
					let ObserveFlag = receiver[Object.getOwnPropertySymbols(obj)[0]]
					if (listeners[ObserveFlag]) {
						listeners[ObserveFlag].add(prop)
					} else {
						listeners[ObserveFlag] = new Set([prop])
					}
				}

				return Reflect.get(obj, prop, receiver);
			},
			set: (obj, prop, value, receiver) => {
				let newValue = Reflect.set(obj, prop, value, receiver);
				// 这里通过是否必须通过action触发更改控制
				if (!isCollect && (typeof prop == 'string')) {
					if (enforceActions) {
						throw Error("当前enforceActions 模式无法对数据进行修改")
					}
					let ObserveFlag = receiver[Object.getOwnPropertySymbols(obj)[0]]
					if (observer[ObserveFlag] && observer[ObserveFlag][prop]) {
						let applys = observer[ObserveFlag][prop];
						applys.forEach(fn => {
							fn.apply(this);
						})
					}

				}
				return newValue;
			}
		})
		proxy[Symbol(observeObj)] = observeObj;
		return proxy;

	}
}
/**
 * 回调缓存
 */
let currentCallback;
// NOTE: 暂时不需要
let isFirst = true;
/**
 * 正在收集side-effects
 */
let isCollect = true;

// NOTE: 这里暂时没实现 
class Reaction {
	constructor(type, callback) {
		// callback.call(actions);
		this.callback = callback.bind(this);
	}
	// 这里初始化，收集依赖
	track(fn) {
		observer = {}; //这里是覆盖收集，所以需要清除所有的依赖
		beforCollect(this.callback);
		fn();
		endCollect();
	}
}

function beforCollect(callback) {
	observerTemp = {};
	isCollect = true;
	currentCallback = callback;
}
function endCollect() {
	isCollect = false;
	// 开始组装observer
	Object.keys(observerTemp).map(observe => {
		Object.keys(observerTemp[observe]).map(prop => {
			// 做容错初始化
			if (!observer[observe]) {
				observer[observe] = {}
			}
			// 注入回调
			if (observer[observe][prop]) {
				observer[observe][prop].push(observerTemp[observe][prop])
			} else {
				observer[observe][prop] = [observerTemp[observe][prop]]
			}
		})

	})
}
function autorun(callback) {
	beforCollect(callback);
	callback();
	endCollect();
}

/**
 * action 等动作时先将操作记录到更新队列，endAction阶段统一执行更新程序
 * 主要是将操作记录
 */
let listenerFlag = false;
/**
 * 记录所有的回调操作，通过 { key , function }的形式
 */
let listeners = {};
function startAction() {
	// 开始收集依赖
	listeners = {};
	listenerFlag = true;
	isCollect = true;
}
/**
 * 
listeners : {
	ObservableObject@1 : ['num','grad']
}
 */


function endAction() {
	// 执行更新队列
	Object.keys(listeners).forEach(observe => {

		listeners[observe].forEach(prop => {
			// eg. ['name','key']
			if (!observer[observe] || (!observer[observe][prop])) return;
			if (Array.isArray(observer[observe][prop])) {
				let applys = observer[observe][prop];
				applys.forEach(fn => {
					fn.apply(this);
				})
			}
		})
	})
	listenerFlag = false;
	isCollect = false;
}
function action(type, fn) {
	// 参数处理,兼容如果没有传类型
	if (typeof type == 'function') {
		fn = type;
	}
	// console.log("注册的action：", type)
	return function () {
		startAction(); //这里开始放入队列
		fn.apply(this);
		endAction(); //释放状态
	}
}

// TODO: 这里暂时只区分true和false
let enforceActions = false;

function configure(config) {
	enforceActions = config.enforceActions;
}
// ------------ 装饰器封装 -------------------



module.exports = {
	Reaction,
	autorun,
	action,
	configure,
	observable
};